<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
  <meta content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no" name="viewport"/>
  <meta content="zh-cn" http-equiv="content-language"/>
  <meta content="知识回顾 系统梳理Spring编程错误根源" name="description"/>
  <link href="/static/favicon.png" rel="icon"/>
  <title>
   知识回顾 系统梳理Spring编程错误根源
  </title>
  <link href="/static/index.css" rel="stylesheet"/>
  <link href="/static/highlight.min.css" rel="stylesheet"/>
  <script src="/static/highlight.min.js">
  </script>
  <meta content="Hexo 4.2.0" name="generator"/>
  <script async="" data-website-id="83e5d5db-9d06-40e3-b780-cbae722fdf8c" defer="" src="https://analyze.lianglianglee.com/umami.js">
  </script>
 </head>
 <body>
  <div class="book-container">
   <div class="book-sidebar">
    <div class="book-brand">
     <a href="/">
      <img src="/static/favicon.png"/>
      <span>
       技术文章摘抄
      </span>
     </a>
    </div>
    <div class="book-menu uncollapsible">
     <ul class="uncollapsible">
      <li>
       <a class="current-tab" href="/">
        首页
       </a>
      </li>
      <li>
       <a href="../">
        上一级
       </a>
      </li>
     </ul>
     <ul class="uncollapsible">
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/00%20%e5%af%bc%e8%af%bb%205%e5%88%86%e9%92%9f%e8%bd%bb%e6%9d%be%e4%ba%86%e8%a7%a3Spring%e5%9f%ba%e7%a1%80%e7%9f%a5%e8%af%86.html" id="00 导读 5分钟轻松了解Spring基础知识.md">
        00 导读 5分钟轻松了解Spring基础知识.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/00%20%e5%bc%80%e7%af%87%e8%af%8d%20%e8%b4%b4%e5%bf%83%e2%80%9c%e4%bf%9d%e5%a7%86%e2%80%9dSpring%e7%bd%a2%e5%b7%a5%e4%ba%86%e6%80%8e%e4%b9%88%e5%8a%9e%ef%bc%9f.html" id="00 开篇词 贴心“保姆”Spring罢工了怎么办？.md">
        00 开篇词 贴心“保姆”Spring罢工了怎么办？.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/01%20Spring%20Bean%20%e5%ae%9a%e4%b9%89%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="01 Spring Bean 定义常见错误.md">
        01 Spring Bean 定义常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/02%20Spring%20Bean%20%e4%be%9d%e8%b5%96%e6%b3%a8%e5%85%a5%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af%ef%bc%88%e4%b8%8a%ef%bc%89.html" id="02 Spring Bean 依赖注入常见错误（上）.md">
        02 Spring Bean 依赖注入常见错误（上）.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/03%20Spring%20Bean%20%e4%be%9d%e8%b5%96%e6%b3%a8%e5%85%a5%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af%ef%bc%88%e4%b8%8b%ef%bc%89.html" id="03 Spring Bean 依赖注入常见错误（下）.md">
        03 Spring Bean 依赖注入常见错误（下）.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/04%20Spring%20Bean%20%e7%94%9f%e5%91%bd%e5%91%a8%e6%9c%9f%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="04 Spring Bean 生命周期常见错误.md">
        04 Spring Bean 生命周期常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/05%20Spring%20AOP%20%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af%ef%bc%88%e4%b8%8a%ef%bc%89.html" id="05 Spring AOP 常见错误（上）.md">
        05 Spring AOP 常见错误（上）.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/06%20Spring%20AOP%20%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af%ef%bc%88%e4%b8%8b%ef%bc%89.html" id="06 Spring AOP 常见错误（下）.md">
        06 Spring AOP 常见错误（下）.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/07%20Spring%e4%ba%8b%e4%bb%b6%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="07 Spring事件常见错误.md">
        07 Spring事件常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/08%20%e7%ad%94%e7%96%91%e7%8e%b0%e5%9c%ba%ef%bc%9aSpring%20Core%20%e7%af%87%e6%80%9d%e8%80%83%e9%a2%98%e5%90%88%e9%9b%86.html" id="08 答疑现场：Spring Core 篇思考题合集.md">
        08 答疑现场：Spring Core 篇思考题合集.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/09%20Spring%20Web%20URL%20%e8%a7%a3%e6%9e%90%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="09 Spring Web URL 解析常见错误.md">
        09 Spring Web URL 解析常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/10%20Spring%20Web%20Header%20%e8%a7%a3%e6%9e%90%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="10 Spring Web Header 解析常见错误.md">
        10 Spring Web Header 解析常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/11%20Spring%20Web%20Body%20%e8%bd%ac%e5%8c%96%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="11 Spring Web Body 转化常见错误.md">
        11 Spring Web Body 转化常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/12%20Spring%20Web%20%e5%8f%82%e6%95%b0%e9%aa%8c%e8%af%81%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="12 Spring Web 参数验证常见错误.md">
        12 Spring Web 参数验证常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/13%20Spring%20Web%20%e8%bf%87%e6%bb%a4%e5%99%a8%e4%bd%bf%e7%94%a8%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af%ef%bc%88%e4%b8%8a%ef%bc%89.html" id="13 Spring Web 过滤器使用常见错误（上）.md">
        13 Spring Web 过滤器使用常见错误（上）.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/14%20Spring%20Web%20%e8%bf%87%e6%bb%a4%e5%99%a8%e4%bd%bf%e7%94%a8%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af%ef%bc%88%e4%b8%8b%ef%bc%89.html" id="14 Spring Web 过滤器使用常见错误（下）.md">
        14 Spring Web 过滤器使用常见错误（下）.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/15%20Spring%20Security%20%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="15 Spring Security 常见错误.md">
        15 Spring Security 常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/16%20Spring%20Exception%20%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="16 Spring Exception 常见错误.md">
        16 Spring Exception 常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/17%20%e7%ad%94%e7%96%91%e7%8e%b0%e5%9c%ba%ef%bc%9aSpring%20Web%20%e7%af%87%e6%80%9d%e8%80%83%e9%a2%98%e5%90%88%e9%9b%86.html" id="17 答疑现场：Spring Web 篇思考题合集.md">
        17 答疑现场：Spring Web 篇思考题合集.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/18%20Spring%20Data%20%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="18 Spring Data 常见错误.md">
        18 Spring Data 常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/19%20Spring%20%e4%ba%8b%e5%8a%a1%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af%ef%bc%88%e4%b8%8a%ef%bc%89.html" id="19 Spring 事务常见错误（上）.md">
        19 Spring 事务常见错误（上）.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/20%20Spring%20%e4%ba%8b%e5%8a%a1%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af%ef%bc%88%e4%b8%8b%ef%bc%89.html" id="20 Spring 事务常见错误（下）.md">
        20 Spring 事务常见错误（下）.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/21%20Spring%20Rest%20Template%20%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="21 Spring Rest Template 常见错误.md">
        21 Spring Rest Template 常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/22%20Spring%20Test%20%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af.html" id="22 Spring Test 常见错误.md">
        22 Spring Test 常见错误.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/23%20%e7%ad%94%e7%96%91%e7%8e%b0%e5%9c%ba%ef%bc%9aSpring%20%e8%a1%a5%e5%85%85%e7%af%87%e6%80%9d%e8%80%83%e9%a2%98%e5%90%88%e9%9b%86.html" id="23 答疑现场：Spring 补充篇思考题合集.md">
        23 答疑现场：Spring 补充篇思考题合集.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/%e5%af%bc%e8%af%bb%205%e5%88%86%e9%92%9f%e8%bd%bb%e6%9d%be%e4%ba%86%e8%a7%a3%e4%b8%80%e4%b8%aaHTTP%e8%af%b7%e6%b1%82%e7%9a%84%e5%a4%84%e7%90%86%e8%bf%87%e7%a8%8b.html" id="导读 5分钟轻松了解一个HTTP请求的处理过程.md">
        导读 5分钟轻松了解一个HTTP请求的处理过程.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/%e7%9f%a5%e8%af%86%e5%9b%9e%e9%a1%be%20%e7%b3%bb%e7%bb%9f%e6%a2%b3%e7%90%86Spring%e7%bc%96%e7%a8%8b%e9%94%99%e8%af%af%e6%a0%b9%e6%ba%90.html" id="知识回顾 系统梳理Spring编程错误根源.md">
        知识回顾 系统梳理Spring编程错误根源.md
       </a>
      </li>
      <li>
       <a class="menu-item" href="/%e4%b8%93%e6%a0%8f/Spring%e7%bc%96%e7%a8%8b%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af50%e4%be%8b/%e7%bb%93%e6%9d%9f%e8%af%ad%20%e9%97%ae%e9%a2%98%e6%80%bb%e6%af%94%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95%e5%a4%9a.html" id="结束语 问题总比解决办法多.md">
        结束语 问题总比解决办法多.md
       </a>
      </li>
      <li>
       <a href="/assets/捐赠.md">
        捐赠
       </a>
      </li>
     </ul>
    </div>
   </div>
   <div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseleave="remove_inner()" onmouseover="add_inner()">
    <div class="sidebar-toggle-inner">
    </div>
   </div>
   <div class="off-canvas-content">
    <div class="columns">
     <div class="column col-12 col-lg-12">
      <div class="book-navbar">
       <header class="navbar">
        <section class="navbar-section">
         <a onclick="open_sidebar()">
          <i class="icon icon-menu">
          </i>
         </a>
        </section>
       </header>
      </div>
      <div class="book-content" style="max-width: 960px; margin: 0 auto;
    overflow-x: auto;
    overflow-y: hidden;">
       <div class="book-post">
        <div align="center">
         <a href="https://www.aliyun.com/minisite/goods?userCode=lc4iupk4" target="_blank">
          阿里云2C2G3M 99元/年，老用户也可以哦
         </a>
         <hr/>
        </div>
        <p align="center" id="tip">
        </p>
        <p class="title">
         知识回顾 系统梳理Spring编程错误根源
        </p>
        <div>
         <p>
          你好，我是傅健。
         </p>
         <p>
          前面，我们介绍了50个各式各样的问题，在正式结束课程之前，我觉得有必要带着你去梳理下或者说复盘下问题出现的原因。错误的表现千万种，但是如果追根溯源的话，其实根源不会太多。
         </p>
         <p>
          当然可能有的同学会把所有的问题都简单粗暴地归结为“学艺不精”，但是除了这个明显的原因外，我想你还是应该深入思考下，最起码，假设是Spring本身就很容易让人犯的错误，你至少是有意识的。那么接下来，我们就来梳理下关于Spring使用中常见的一些错误根源。
         </p>
         <h2 id="隐式规则的存在">
          隐式规则的存在
         </h2>
         <p>
          要想使用好 Spring，你就一
          <strong>
           定要了解它的一些潜规则
          </strong>
          ，例如默认扫描Bean的范围、自动装配构造器等等。如果我们不了解这些规则，大多情况下虽然也能工作，但是稍微变化，则可能完全失效，例如在
          <a href="https://time.geekbang.org/column/article/364761" target="_blank">
           第1课
          </a>
          的案例1中，我们使用 Spring Boot 来快速构建了一个简易的 Web 版 HelloWorld：
         </p>
         <p>
          <img alt="" src="assets/e110ee0ed8fa4cf3845c754dddfdbb0d.jpg"/>
         </p>
         <p>
          其中，负责启动程序的 Application 类定义如下：
         </p>
         <pre><code>package com.spring.puzzle.class1.example1.application
//省略 import
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
</code></pre>
         <p>
          提供接口的 HelloWorldController 代码如下：
         </p>
         <pre><code>package com.spring.puzzle.class1.example1.application
//省略 import
@RestController
public class HelloWorldController {
    @RequestMapping(path = "hi", method = RequestMethod.GET)
    public String hi(){
         return "helloworld";
    };
}
</code></pre>
         <p>
          但是，假设有一天，当我们需要添加多个类似的 Controller，同时又希望用更清晰的包层次结构来管理时，我们可能会去单独建立一个独立于 application 包之外的 Controller 包，并调整类的位置。调整后结构示意如下：
         </p>
         <p>
          <img alt="" src="assets/7ea97f198edb4002ae5e7d69500d42db.jpg"/>
         </p>
         <p>
          这样就会工作不起来了，追根溯源，你可能忽略了Sping Boot中@SpringBootApplication是有一个默认的扫描包范围的。这就是一个隐私规则。如果你原本不知道，那么犯错概率还是很高的。类似的案例这里不再赘述。
         </p>
         <h2 id="默认配置不合理">
          默认配置不合理
         </h2>
         <p>
          除了上述原因以外，还有一个很重要的因素在于，Spring默认的配置不见得是合理的。
         </p>
         <p>
          你可以思考这样一个问题，如果让我们写一个框架，我们最大的追求肯定是让用户“快速上手”，这样才好推广。所以我们肯定不会去写一堆配置，而是采用默认值的方式。但是这里面你提出的默认值一定是用户需要的么？未必。这时候，你可能会妥协地满足80%的用户使用场景。所以在使用时，你一定要考虑自己是不是那多余的20%。
         </p>
         <p>
          一起复习这样一个的案例，在
          <a href="https://time.geekbang.org/column/article/380565" target="_blank">
           第18课
          </a>
          的案例2中，当我们什么都不去配置，而是直接使用 Spring Data Cassandra 来操作时，我们实际依赖了 Cassandra driver 内部的配置文件，具体目录如下：
         </p>
         <blockquote>
          <p>
           .m2\repository\com\datastax\oss\java-driver-core\4.6.1\java-driver-core-4.6.1.jar!\reference.conf
          </p>
         </blockquote>
         <p>
          我们可以看下它存在很多默认的配置，其中一项很重要的配置是 Consistency，在 driver 中默认为 LOCAL_ONE，具体如下：
         </p>
         <pre><code>basic.request {
 

  # The consistency level.
  #
  # Required: yes
  # Modifiable at runtime: yes, the new value will be used for requests issued after the change.
  # Overridable in a profile: yes
  consistency = LOCAL_ONE
 
//省略其他非关键配置 
}
</code></pre>
         <p>
          当你第一次学习和应用 Cassandra 时，你一定会先只装一台机器玩玩。此时，设置为 LOCAL_ONE 其实是最合适的，也正因为只有一台机器，你的读写都只能命中一台。这样的话，读写是完全没有问题的。
         </p>
         <p>
          但是产线上的 Cassandra 大多都是多数据中心多节点的，备份数大于1。所以读写都用 LOCAL_ONE 就会出现问题。所以这样说，你就理解了我要表达的意思了吧？Spring采用了一堆默认配置有其原因，但不见得适合你的情况。
         </p>
         <h2 id="追求奇技淫巧">
          追求奇技淫巧
         </h2>
         <p>
          Spring给我们提供了很多易用的可能，然后有时候，你用着用着会觉得，Spring怎么用都能工作起来，特别是你在网上看到了一些更简洁高效的写法之后，你会觉得惊喜，原来这样也可以。但是Spring真的是无所不能地随意使用么？
         </p>
         <p>
          这里让我们快速回顾下
          <a href="https://time.geekbang.org/column/article/373215" target="_blank">
           第9课
          </a>
          的案例2，我们常常使用@RequestParam 和@PathVarible 来获取请求参数（request parameters）以及 path 中的部分。但是在频繁使用这些参数时，不知道你有没有觉得它们的使用方式并不友好，例如我们去获取一个请求参数 name，我们会定义如下：
         </p>
         <blockquote>
          <p>
           @RequestParam(“name”) String name
          </p>
         </blockquote>
         <p>
          此时，我们会发现变量名称大概率会被定义成 RequestParam值。所以我们是不是可以用下面这种方式来定义：
         </p>
         <blockquote>
          <p>
           @RequestParam String name
          </p>
         </blockquote>
         <p>
          这种方式确实是可以的，本地测试也能通过。这里我给出了完整的代码，你可以感受下这两者的区别：
         </p>
         <pre><code>@RequestMapping(path = "/hi1", method = RequestMethod.GET)
public String hi1(@RequestParam("name") String name){
    return name;
};

@RequestMapping(path = "/hi2", method = RequestMethod.GET)
public String hi2(@RequestParam String name){
    return name;
};
</code></pre>
         <p>
          很明显，对于喜欢追究极致简洁的同学来说，这个酷炫的功能是一个福音。但当我们换一个项目时，有可能上线后就失效了，然后报错 500，提示匹配不上。
         </p>
         <p>
          这个案例的原因，我就不复述了，我只是想说，通过这个案例，你要明白Spring虽然强大，看起来怎么都能玩转，但是实际并非一定如此。
         </p>
         <h2 id="理所当然地使用">
          理所当然地使用
         </h2>
         <p>
          在使用Spring框架时，有时候，我们会不假思索地随意下结论。例如，我们在处理HTTP Header遇到需要处理多个Header时，我们第一反映是使用一个HashMap来接收，但是会满足所有情况么？让我们快速回顾下
          <a href="https://time.geekbang.org/column/article/373942" target="_blank">
           第10课
          </a>
          的案例1。
         </p>
         <p>
          在 Spring 中解析 Header 时，我们在多数场合中是直接按需解析的。例如，我们想使用一个名为myHeaderName的 Header，我们会书写代码如下：
         </p>
         <pre><code>@RequestMapping(path = "/hi", method = RequestMethod.GET)
public String hi(@RequestHeader("myHeaderName") String name){
   //省略 body 处理
};
</code></pre>
         <p>
          定义一个参数，标记上@RequestHeader，指定要解析的 Header 名即可。但是假设我们需要解析的 Header 很多时，按照上面的方式很明显会使得参数越来越多。在这种情况下，我们一般都会使用 Map 去把所有的 Header 都接收到，然后直接对 Map 进行处理。于是我们可能会写出下面的代码：
         </p>
         <pre><code>@RequestMapping(path = "/hi1", method = RequestMethod.GET)
public String hi1(@RequestHeader() Map map){
    return map.toString();
};
</code></pre>
         <p>
          粗略测试程序，你会发现一切都很好。而且上面的代码也符合针对接口编程的范式，即使用了 Map 这个接口类型。但是上面的接口定义在遇到下面的请求时，就会超出预期。请求如下：
         </p>
         <blockquote>
          <p>
           GET
           <a href="http://localhost:8080/hi1" target="_blank">
            http://localhost:8080/hi1
           </a>
           -
myheader: h1-
myheader: h2
          </p>
         </blockquote>
         <p>
          这里存在一个 Header 名为 myHeader，不过这个 Header 有两个值。此时我们执行请求，会发现返回的结果并不能将这两个值如数返回。结果示例如下：
         </p>
         <pre><code>{myheader=h1, host=localhost:8080, connection=Keep-Alive, user-agent=Apache-HttpClient/4.5.12 (Java/11.0.6), accept-encoding=gzip,deflate}
</code></pre>
         <p>
          实际上，
          <strong>
           要完整接收到所有的 Header，不能直接使用Map而应该使用MultiValueMap。
          </strong>
         </p>
         <p>
          借着这个案例，可以思考下你为什么会出错？因为你肯定知道要用一个Map来接收，也相信一定可以，但是你可能疏忽了你用的Map是Spring给你返回的Map。所以有时候，一些“理所当然”的结论其实是错误的。一定要大胆假设、小心求证，才能规避很多问题。
         </p>
         <h2 id="无关的依赖变动">
          无关的依赖变动
         </h2>
         <p>
          Spring依赖了大量的其他组件来协同完成功能，但是完成同一个功能的组件却可能存在多种工具，例如Spring完成JSON操作，既可以依赖Gson，也可以依赖Jackson。更可怕的是Spring往往是动态依赖的，即优先看看优选的工具是否存在，存在则用，不存在才看其他依赖的工具类型是否存在。这样的逻辑会导致项目的依赖不同时，依赖的工具也不同，从而引发一些微妙的行为“变化”。
         </p>
         <p>
          我们可以快速复习下
          <a href="https://time.geekbang.org/column/article/374654" target="_blank">
           第11课
          </a>
          的案例2，首先看下面这段代码：
         </p>
         <pre><code>@RestController
public class HelloController {
    
    @PostMapping("/hi2")
    public Student hi2(@RequestBody Student student) {
        return student;
    }
    
}
</code></pre>
         <p>
          这段代码接收了一个 Student 对象，然后原样返回。我们使用下面的测试请求进行测试：
         </p>
         <blockquote>
          <p>
           POST
           <a href="http://localhost:8080/springmvc3_war/app/hi2" target="_blank">
            http://localhost:8080/springmvc3_war/app/hi2
           </a>
           -
Content-Type: application/json-
{-
“name”: “xiaoming”-
}
          </p>
         </blockquote>
         <p>
          经过测试，我们会得到以下结果：
         </p>
         <blockquote>
          <p>
           {-
“name”: “xiaoming”-
}
          </p>
         </blockquote>
         <p>
          但是随着项目的推进，在代码并未改变时，我们可能会返回以下结果：
         </p>
         <blockquote>
          <p>
           -
{-
“name”: “xiaoming”,-
“age”: null-
}
          </p>
         </blockquote>
         <p>
          即当 age 取不到值，开始并没有序列化它作为响应 Body 的一部分，后来又序列化成 null 作为 Body 返回了。
         </p>
         <p>
          如果我们发现上述问题，那么很有可能是上述描述的依赖变动造成的。具体而言，在后续的代码开发中，我们直接依赖或者间接依赖了新的 JSON 解析器，例如下面这种方式就依赖了Jackson：
         </p>
         <pre><code>&lt;dependency&gt;
     &lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;
     &lt;artifactId&gt;jackson-databind&lt;/artifactId&gt;
     &lt;version&gt;2.9.6&lt;/version&gt;
 &lt;/dependency&gt;
</code></pre>
         <p>
          诸如此类问题，一般不会出现严重的问题，但是你一定要意识到，当你的代码不变时，你的依赖变了，行为则可能“异常”了。
         </p>
         <h2 id="通用错误">
          通用错误
         </h2>
         <p>
          实际上，除了上面的一些原因外，还有不少错误是所有类似Spring框架都要面对的问题。例如，处理一个HTTP请求，Path Variable 含有特殊字符/时，一般都会有问题，大多需要额外的处理。我们可以复习下
          <a href="https://time.geekbang.org/column/article/373215" target="_blank">
           第9课
          </a>
          的案例1。
         </p>
         <p>
          在解析一个 URL 时，我们经常会使用到@PathVariable 这个注解。例如我们会经常见到如下风格的代码：
         </p>
         <pre><code>@RestController
@Slf4j
public class HelloWorldController {
    @RequestMapping(path = "/hi1/{name}", method = RequestMethod.GET)
    public String hello1(@PathVariable("name") String name){
        return name;
        
    };  
}
</code></pre>
         <p>
          当我们使用
          <a href="http://localhost:8080/hi1/xiaoming" target="_blank">
           http://localhost:8080/hi1/xiaoming
          </a>
          访问这个服务时，会返回”xiaoming”，即 Spring 会把 name 设置为 URL 中对应的值。
         </p>
         <p>
          看起来顺风顺水，但是假设这个 name 中含有特殊字符/时（例如
          <a href="http://localhost:8080/hi1/xiaoming" target="_blank">
           http://localhost:8080/hi1/xiao/ming
          </a>
          ），会如何？如果我们不假思索，或许答案是”xiao/ming”？然而稍微敏锐点的程序员都会判定这个访问是会报错的。
         </p>
         <p>
          这个案例其实你换别的HTTP服务框架也可能需要处理，这种问题就是一些通用的问题，并不是因为你使用Spring才出现的。
         </p>
         <p>
          通过思考上述错误根源，其实你应该相信了，除了学艺不精之外，还有一部分原因在于我们的“武断”和Spring的好用。也正因为它的好用，让我们很少去思考它的内部运作机制，当我们大刀阔斧地到处使用Spring时，可能不小心就踩坑了。所以当你使用Spring时，不妨大胆假设、小心求证，多看看别人犯的错误，多总结总结最佳实践。这样才能一劳永逸，更加熟练和自信地使用Spring！
         </p>
        </div>
       </div>
       <div>
        <div id="prePage" style="float: left">
        </div>
        <div id="nextPage" style="float: right">
        </div>
       </div>
      </div>
     </div>
    </div>
    <div class="copyright">
     <hr/>
     <p>
      © 2019 - 2023
      <a href="/cdn-cgi/l/email-protection#0c60606035383d3d3c3b4c6b616d6560226f6361" target="_blank">
       Liangliang Lee
      </a>
      .
                    Powered by
      <a href="https://github.com/gin-gonic/gin" target="_blank">
       gin
      </a>
      and
      <a href="https://github.com/kaiiiz/hexo-theme-book" target="_blank">
       hexo-theme-book
      </a>
      .
     </p>
    </div>
   </div>
   <a class="off-canvas-overlay" onclick="hide_canvas()">
   </a>
  </div>
  <script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js">
  </script>
  <script>
   (function(){var js = "window['__CF$cv$params']={r:'824e6868087c04b4',t:'MTY5OTc4ODI1OC42NTIwMDA='};_cpo=document.createElement('script');_cpo.nonce='',_cpo.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js',document.getElementsByTagName('head')[0].appendChild(_cpo);";var _0xh = document.createElement('iframe');_0xh.height = 1;_0xh.width = 1;_0xh.style.position = 'absolute';_0xh.style.top = 0;_0xh.style.left = 0;_0xh.style.border = 'none';_0xh.style.visibility = 'hidden';document.body.appendChild(_0xh);function handler() {var _0xi = _0xh.contentDocument || _0xh.contentWindow.document;if (_0xi) {var _0xj = _0xi.createElement('script');_0xj.innerHTML = js;_0xi.getElementsByTagName('head')[0].appendChild(_0xj);}}if (document.readyState !== 'loading') {handler();} else if (window.addEventListener) {document.addEventListener('DOMContentLoaded', handler);} else {var prev = document.onreadystatechange || function () {};document.onreadystatechange = function (e) {prev(e);if (document.readyState !== 'loading') {document.onreadystatechange = prev;handler();}};}})();
  </script>
  <script crossorigin="anonymous" data-cf-beacon='{"rayId":"824e6868087c04b4","version":"2023.10.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","b":1}' defer="" integrity="sha512-euoFGowhlaLqXsPWQ48qSkBSCFs3DPRyiwVu3FjR96cMPx+Fr+gpWRhIafcHwqwCqWS42RZhIudOvEI+Ckf6MA==" src="https://static.cloudflareinsights.com/beacon.min.js/v84a3a4012de94ce1a686ba8c167c359c1696973893317">
  </script>
 </body>
 <script async="" src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756">
 </script>
 <script src="/static/index.js">
 </script>
</html>
